package com.kuaimacode.kframework.api.service;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.cloudauth.model.v20180916.*;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kuaimacode.kframework.api.util.DownloadImg;
import com.kuaimacode.kframework.api.vo.request.TwoElementsReq;
import com.kuaimacode.kframework.api.vo.response.CertifyTokenResp;
import com.kuaimacode.kframework.enums.YesOrNoEnum;
import com.kuaimacode.kframework.exception.ErrorEnum;
import com.kuaimacode.kframework.exception.ServerInternalErrorException;
import com.kuaimacode.kframework.mybatis.models.user.User;
import com.kuaimacode.kframework.mybatis.models.user.UserCertifacate;
import com.kuaimacode.kframework.util.CommonUtil;
import com.kuaimacode.kframework.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * @author qinyong
 * @Title:
 * @Package com.kuaimacode.kframework.api.service
 * @Description: created by IntelliJ IDEA
 * @date 2019-03-17 20:56
 */
@Slf4j
@Service
public class CertificationService {

    @Value("${aliyuns.api.keyid}")
    private String accessKeyId;
    @Value("${aliyuns.api.secret}")
    private String accessKeySecret;
    /**
     * 您在控制台上创建的、采用RPBasic认证方案的认证场景标识, 创建方法：https://help.aliyun.com/document_detail/59975.html
     */
    @Value("${ali.certificate.scenes}")
    private String certificateScenes;
    @Autowired
    private UserCertifacateService userCertifacateService;
    @Autowired
    private UserService userService;
    @Value("${img.upload.location.root}")
    private String uploadRootLocation;
    @Value("${img.upload.location.img}")
    private String imgUploadLocation;

    /**
     * 获取认证需要的token
     *
     * @return
     */
    public CertifyTokenResp getToken() {
        //创建DefaultAcsClient实例并初始化
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-hangzhou",
                accessKeyId,
                accessKeySecret);
        IAcsClient client = new DefaultAcsClient(profile);
        //认证ID, 由使用方指定, 发起不同的认证任务需要更换不同的认证ID
        String ticketId = UUID.randomUUID().toString();
        //1. 服务端发起认证请求, 获取到token
        //GetVerifyToken接口文档：https://help.aliyun.com/document_detail/57050.html
        GetVerifyTokenRequest getVerifyTokenRequest = new GetVerifyTokenRequest();
        getVerifyTokenRequest.setBiz(certificateScenes);
        getVerifyTokenRequest.setTicketId(ticketId);
        try {
            GetVerifyTokenResponse response = client.getAcsResponse(getVerifyTokenRequest);
            //token默认30分钟时效，每次发起认证时都必须实时获取
            CertifyTokenResp tokenResp = new CertifyTokenResp();
            tokenResp.setTicketId(ticketId);
            tokenResp.setToken(response.getData().getVerifyToken().getToken());
            return tokenResp;
        } catch (ClientException e) {
            log.error("获取实名认证token失败", e);
            throw new ServerInternalErrorException(ErrorEnum.GET_CERTIFICATE_TOKEN_ERROR);
        }
    }

    /**
     * 查询认证状态
     *
     * @param ticketId
     * @param userId
     * @return
     */
    public boolean getStatus(String ticketId, Long userId) {
        //创建DefaultAcsClient实例并初始化
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-hangzhou",
                accessKeyId,
                accessKeySecret);
        IAcsClient client = new DefaultAcsClient(profile);
        GetStatusRequest getStatusRequest = new GetStatusRequest();
        getStatusRequest.setBiz(certificateScenes);
        getStatusRequest.setTicketId(ticketId);
        try {
            GetStatusResponse response = client.getAcsResponse(getStatusRequest);
            //获取认证资料
            getCertifyData(response.getData().getStatusCode(), ticketId, client, userId);
            //1代表认证通过，2代表认证不通过
            return response.getData().getStatusCode() == 1;
        } catch (ClientException e) {
            log.error("查询认证状态失败", e);
            throw new ServerInternalErrorException(ErrorEnum.GET_CERTIFY_STATUS_ERROR);
        }
    }

    /**
     * 保存用户两要素信息
     * @param twoElementsReq
     */
    public boolean saveTwoElements(TwoElementsReq twoElementsReq, Long userId) {
        UserCertifacate certifacate = userCertifacateService.selectByUserId(userId);
        boolean save = false;
        if (certifacate == null) {
            certifacate = new UserCertifacate();
            save = true;
        }
        certifacate.setUserId(userId);
        certifacate.setCardPositive(twoElementsReq.getPosImgName());
        certifacate.setCardNegative(twoElementsReq.getNegImgName());
        certifacate.setIdCard(twoElementsReq.getIdCard());
        certifacate.setName(twoElementsReq.getName());
        if (save) {
            return userCertifacateService.save(certifacate);
        } else {
            return userCertifacateService.updateById(certifacate);
        }
    }

    /**
     * 保存实名认证头部照片
     * @param headImgName
     * @param userId
     * @return
     */
    public boolean saveCertiHead(String headImgName, Long userId) {
        QueryWrapper<UserCertifacate> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        UserCertifacate updateUser = new UserCertifacate();
        updateUser.setHeadPic(headImgName);
        return userCertifacateService.update(updateUser, queryWrapper);
    }

    /**
     * 实名认证后获取认证资料
     * @param statusCode
     * @param ticketId
     * @param client
     */
    private void getCertifyData(Integer statusCode, String ticketId, IAcsClient client, Long userId) {
        GetMaterialsRequest getMaterialsRequest = new GetMaterialsRequest();
        getMaterialsRequest.setBiz(certificateScenes);
        getMaterialsRequest.setTicketId(ticketId);
        //认证通过or认证不通过
        if (1 == statusCode || 2 == statusCode) {
            try {
                GetMaterialsResponse response = client.getAcsResponse(getMaterialsRequest);
                UserCertifacate userCertifacate = new UserCertifacate();
                GetMaterialsResponse.Data data = response.getData();
                userCertifacate.setName(data.getName());
                userCertifacate.setIdCard(data.getIdentificationNumber());
                //下载身份证图片
                String negName = DateUtil.getNow(DateUtil.FORMAT_FOUR_MINUTE) + "_" + userId + "neg.jpg";
                DownloadImg.writeImageToDisk(DownloadImg.getImageFromNetByUrl(data.getIdCardBackPic()),
                        uploadRootLocation + imgUploadLocation, negName);
                String posName = DateUtil.getNow(DateUtil.FORMAT_FOUR_MINUTE) + "_" + userId + "pos.jpg";
                DownloadImg.writeImageToDisk(DownloadImg.getImageFromNetByUrl(data.getIdCardFrontPic()),
                        uploadRootLocation + imgUploadLocation, posName);
                String headName = DateUtil.getNow(DateUtil.FORMAT_FOUR_MINUTE) + "_" + userId + "head.jpg";
                DownloadImg.writeImageToDisk(DownloadImg.getImageFromNetByUrl(data.getFacePic()),
                        uploadRootLocation + imgUploadLocation, headName);
                userCertifacate.setCardNegative(negName);
                userCertifacate.setCardPositive(posName);
                userCertifacate.setHeadPic(headName);
                userCertifacate.setUserId(userId);
                userCertifacateService.save(userCertifacate);
                //后续业务处理
            } catch (ClientException e) {
                log.error("获取阿里云认证资料失败, userId: {}", userId, e);
                return;
            }
        }
        //认证通过更新用户认证状态
        if (1 == statusCode) {
            User updateUser = new User();
            updateUser.setId(userId);
            updateUser.setIsCertifacate(YesOrNoEnum.YES.getValue());
            userService.updateById(updateUser);
        }
    }

    /**
     * 纯服务端实名认证
     * @param userId
     */
    public void rPMinCertify(Long userId) {
        Optional.ofNullable(userService.getById(userId))
                .filter(user -> !user.isCertificated())
                .orElseThrow(() -> new ServerInternalErrorException(ErrorEnum.USER_ALREADY_CERTIFY));

        //获取上传的资料信息
        UserCertifacate certifacate = userCertifacateService.selectByUserId(userId);
        if (StringUtils.isAnyBlank(certifacate.getName(), certifacate.getIdCard(), certifacate.getCardPositive(), certifacate.getCardNegative(), certifacate.getHeadPic())) {
            throw new ServerInternalErrorException(ErrorEnum.CERTIFY_DATA_NOT_COMPLETE);
        }

        //创建DefaultAcsClient实例并初始化
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-hangzhou", accessKeyId, accessKeySecret);
        IAcsClient client = new DefaultAcsClient(profile);
        String biz = "smallAPP";
        String ticketId = UUID.randomUUID().toString();
        //提交认证材料
        SubmitVerificationRequest submitRequest = new SubmitVerificationRequest();
        submitRequest.setBiz(biz);
        submitRequest.setTicketId(ticketId);
        //创建要提交的认证材料列表, 请根据 认证方案 中的说明传入相应字段
        List<SubmitVerificationRequest.Material> verifyMaterials = new ArrayList<>();
        SubmitVerificationRequest.Material identificationNumber = new SubmitVerificationRequest.Material();
        identificationNumber.setMaterialType("IdentificationNumber");
        identificationNumber.setValue(certifacate.getIdCard());
        verifyMaterials.add(identificationNumber);
        SubmitVerificationRequest.Material name = new SubmitVerificationRequest.Material();
        name.setMaterialType("Name");
        name.setValue(certifacate.getName());
        verifyMaterials.add(name);
        //传入图片资料，请控制单张图片大小在 2M 内，避免拉取超时
        SubmitVerificationRequest.Material facePic = new SubmitVerificationRequest.Material();
        facePic.setMaterialType("FacePic");
        facePic.setValue("base64://" + CommonUtil.imageToBase64(fillImagePath(certifacate.getHeadPic())));
        verifyMaterials.add(facePic);
        SubmitVerificationRequest.Material idCardFrontPic = new SubmitVerificationRequest.Material();
        idCardFrontPic.setMaterialType("IdCardFrontPic");
        idCardFrontPic.setValue("base64://" + CommonUtil.imageToBase64(fillImagePath(certifacate.getCardPositive())));
        verifyMaterials.add(idCardFrontPic);
        SubmitVerificationRequest.Material idCardBackPic = new SubmitVerificationRequest.Material();
        idCardBackPic.setMaterialType("IdCardBackPic");
        idCardBackPic.setValue("base64://" + CommonUtil.imageToBase64(fillImagePath(certifacate.getCardNegative())));
        verifyMaterials.add(idCardBackPic);
        submitRequest.setMaterials(verifyMaterials);

        SubmitVerificationResponse response;
        try {
            response = client.getAcsResponse(submitRequest);
        //后续业务处理
        } catch (Exception e) {
            log.error("纯服务端实名认证失败, userId: {}", userId, e);
            throw new ServerInternalErrorException(ErrorEnum.CALL_ALI_SERVICE_ERROR);
        }
        SubmitVerificationResponse.Data.VerifyStatus verifyStatus = response.getData().getVerifyStatus();
        if (verifyStatus != null && verifyStatus.getStatusCode() == 1) {

            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("id", userId);
            wrapper.eq("is_certifacate", 0);

            User updateUser = new User();
            updateUser.setIsCertifacate(YesOrNoEnum.YES.getValue());
            if (!userService.update(updateUser, wrapper)) {
                throw new ServerInternalErrorException(ErrorEnum.USER_ALREADY_CERTIFY);
            }
        } else {
            verifyStatus = Optional.ofNullable(verifyStatus).orElseThrow(() -> new ServerInternalErrorException(ErrorEnum.CERTIFY_ERROR));
            throw new ServerInternalErrorException(verifyStatus.getStatusCode().toString(), verifyStatus.getAuditConclusions());
        }
        //常见问题：https://help.aliyun.com/document_detail/57640.html
    }

    private String fillImagePath(String fileName) {
        return uploadRootLocation + imgUploadLocation + fileName;
    }
}
